using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using ExtendedMvc.Common.Diagnostics;
using ExtendedMvc.Html;

namespace ExtendedMvc.Elements {
	public abstract class Selection<T, TModel> : FormElement<T, TModel>
		where T : Selection<T, TModel>
		where TModel : class {
		private IDictionary<object, string> _options;
		private IList<object> _selection;
		private bool _isMultiSelect;
		private string _onSelect;

		protected IDictionary<object, string> options { get { return _options; } }

		protected Selection(IModelViewHelper<TModel> helper, string tag, string id, string memberName, bool isMultiSelect)
			: base(helper, tag, id, memberName) {
			_isMultiSelect = isMultiSelect;
		}

		public virtual T Options(MultiSelectList values) {
			Check.Argument.IsNotNull(values, "values");
			_options = values.ToDictionary(i => (object)i.Value, i => i.Text);
			return (T)this;
		}

		public virtual T Options(IEnumerable values, string valueField, string textField) {
			Check.Argument.IsNotNull(values, "values");
			Check.Argument.IsNotNullOrWhiteSpace(valueField, "valueField");
			Check.Argument.IsNotNullOrWhiteSpace(textField, "textField");
			Options(new MultiSelectList(values, valueField, textField));
			return (T)this;
		}

		public virtual T Options(IDictionary<object, string> values) {
			Check.Argument.IsNotNull(values, "values");
			_options = values.ToDictionary(i => i.Key, i => i.Value);
			return (T)this;
		}

		public virtual T Options(IEnumerable<object> values) {
			Check.Argument.IsNotNull(values, "values");
			_options = values.ToDictionary(i => i, i => i.ToString());
			return (T)this;
		}

		public virtual T Options<TEnum>() where TEnum : struct {
			Check.Argument.IsNotValid(null, "TEnum", p => { return typeof(TEnum).IsEnum; }, "The generic parameter must be an enum");
			var dict = new Dictionary<object, string>();
			var values = Enum.GetValues(typeof(TEnum));
			_options = values.Cast<TEnum>().ToDictionary(i => (object)i, i => i.ToString());
			return (T)this;
		}

		public virtual T Select(object value) {
			if (value == null) _selection = null;
			if (!_isMultiSelect && (value is IEnumerable<object> || value is IDictionary<object, object>)) throw new ArgumentException("This control only accepts single selection.", "Select");
			if (value is IEnumerable<object>) _selection = new List<object>(value as IEnumerable<object>);
			else if (value is IDictionary<object, object>) _selection = new List<object>((value as IDictionary<object, object>).Keys);
			else _selection = new List<object>() { value };
			return (T)this;
		}

		protected virtual bool IsSelected(object value) {
			if (_selection == null) return false;
			return _selection.Any(v => v != null && v.Equals(value));
		}

		public virtual T OnSelect(string value) {
			_onSelect = value;
			return (T)this;
		}

		protected override TagRenderMode TagRenderMode { get { return TagRenderMode.Normal; } }

		protected override void SetId() {
			if (string.IsNullOrWhiteSpace(id)) base.Id(memberName);
			base.SetId();
		}

		protected override void Prepare() {
			if (!string.IsNullOrWhiteSpace(_onSelect)) SetAttribute(HtmlEventAttribute.OnSelect, _onSelect);
			base.Prepare();
		}

		protected override void SetContent() {
			Builder.InnerHtml = RenderOptions();
		}

		protected abstract string RenderOptions();
	}
}
